#include <SD.h>
#include <WiFi.h>
#include <WiFiClient.h>
#include <WiFiServer.h>

//Config start v2.1
  /* change this to match your SD Card shield or module;
   - Arduino Ethernet shield: pin 4
   - Adafruit SD shields and modules: pin 10
   - Sparkfun SD shield: pin 8
   */
  const int chipSelect = 4;  
//Config end

// Globals
  boolean WIFI_SHIELD = false;
  boolean NETWORKING  = false;

//Properties start
  char* CONFIGURED   = NULL;
  char* WIFI_SSID    = NULL;
  char* WIFI_PASS    = NULL;
//Properties end

void setup()
{
  // Open serial communications and wait for port to open:
  Serial.begin(9600);
   while (!Serial) {
    ; // wait for serial port to connect. Needed for Leonardo only
  }
  //init SD Card
  if(initSDCard()) {
    //load configuration from SD
    loadConfiguration();
    // configure networking
    configureWiFi();
    //save configuration to SD
    saveConfiguration();
  }
}

void loop()
{
  if(!CONFIGURED)  {
    delay(10000);
    Serial.println("INFO: Not configured.");
    return;
  }
  Serial.println("INFO: configured.");
  Serial.println(freeRam());
  delay(10000);
}

boolean initSDCard () {
  // The hardware SS pin (10 on most Arduino boards, 53 on the Mega) 
  // must be left as an output or the SD library functions will not work. 
  pinMode(53, OUTPUT);

  if (!SD.begin(chipSelect)) {
    Serial.println("ERROR:[SD Card] Initialization failed.");
    return false;
  }
  
  // init SD card and print the type of card
  Sd2Card card;
  if (card.init(SPI_HALF_SPEED, chipSelect)) {
    Serial.print("INFO:[SD Card] Card type: ");
    switch(card.type()) {
      case SD_CARD_TYPE_SD1:
        Serial.println("SD1");
        break;
      case SD_CARD_TYPE_SD2:
        Serial.println("SD2");
        break;
      case SD_CARD_TYPE_SDHC:
        Serial.println("SDHC");
        break;
      default:
        Serial.println("Unknown");
    }
  }
  
  // Now we will try to open the 'volume'/'partition' - it should be FAT16 or FAT32
  SdVolume volume;
  if (!volume.init(card)) {
    Serial.println("ERROR:[SD Card] Could not find FAT16/FAT32 partition.");
    return false;
  }
  
  // print the type and size of the first FAT-type volume
  Serial.print("INFO:[SD Card] Volume type: FAT");
  Serial.print(volume.fatType(), DEC);
  uint32_t volumesize;
  volumesize = volume.blocksPerCluster();    // clusters are collections of blocks
  volumesize *= volume.clusterCount();       // we'll have a lot of clusters
  volumesize *= 512;                         // SD card blocks are always 512 bytes
  Serial.print(", Volume size (MB): ");
  volumesize /= 1048576;                     //1024*1024
  Serial.println(volumesize); 
  return true;
}

void loadConfiguration() {
  if (!SD.exists("config.txt")) {
    Serial.println("ERROR:[SD Card] config.txt not found.");
    return;
  }
  
  // read properties from config.txt
  File configFile = SD.open("config.txt", FILE_READ);
  if (configFile) {
    char prop_name[50];
    char prop_value[100];
    int prop_name_idx = 0;
    int prop_value_idx = 0;
    boolean prop_separator_found = false;
    while (configFile.available()) {
      char c = configFile.read();
      if(c=='\n' && prop_name_idx!=0) {
        if(prop_name_idx>sizeof(prop_name))
          prop_name_idx=sizeof(prop_name);
        prop_name[prop_name_idx]='\0';
        if(prop_value_idx>sizeof(prop_value))
          prop_value_idx=sizeof(prop_value);
        // set application properties
        if(strcmp(prop_name,"CONFIGURED")==0) {
          CONFIGURED = (char*)malloc((prop_value_idx+1)*sizeof(char));
          strncpy(CONFIGURED,prop_value,prop_value_idx);
          CONFIGURED[prop_value_idx-1]='\0';
        }
        if(strcmp(prop_name, "WIFI_SSID")==0) {
          WIFI_SSID = (char*)malloc((prop_value_idx+1)*sizeof(char));
          strncpy(WIFI_SSID,prop_value,prop_value_idx);
          WIFI_SSID[prop_value_idx-1]='\0';
        }
        if(strcmp(prop_name, "WIFI_PASS")==0) {
          WIFI_PASS = (char*)malloc((prop_value_idx+1)*sizeof(char));
          strncpy(WIFI_PASS,prop_value,prop_value_idx);
          WIFI_PASS[prop_value_idx-1]='\0';
        }  
        prop_name_idx=0;    
        prop_value_idx=0;
        prop_separator_found = false;
      }
      else if(c==':' && !prop_separator_found) {
        prop_separator_found = true;
      }
      else {
        if(!prop_separator_found){
          if(prop_name_idx<sizeof(prop_name))
            prop_name[prop_name_idx]=c;
            prop_name_idx++;
        }
        else {
          if(prop_value_idx<sizeof(prop_value))
            prop_value[prop_value_idx]=c;
            prop_value_idx++;
        }
      }
    }
    configFile.close();
    Serial.println("INFO:[SD Card] Configurations from config.txt loaded.");
  }
  return;
}

boolean saveConfiguration() {
  pinMode(53, OUTPUT);

  if (!SD.begin(chipSelect)) {
    Serial.println("ERROR:[SD Card] Initialization failed.");
    return false;
  }
  File configFile = SD.open("config.txt", FILE_WRITE);
  if (configFile) {
    configFile.print("CONFIGURED=");
    configFile.println(CONFIGURED);
    configFile.print("WIFI_SSID=");
    configFile.println(WIFI_SSID);
    configFile.print("WIFI_PASS=");
    configFile.println(WIFI_PASS);
    configFile.println("WIFI_PASS2=PASS2");
    configFile.close();
    delay(1000);
  }
  else { 
    Serial.println("ERROR:[SD Card] Error opening config.txt file.");
    return false;
  }
  Serial.println("INFO:[SD Card] Configuration saved.");
  return true;
}

void configureWiFi() {
  if (WiFi.status() == WL_NO_SHIELD) {
    Serial.println("ERROR:[WiFi] shield not present.");
    return;
  }
  int status = WL_IDLE_STATUS;     // the Wifi radio's status
  Serial.print("INFO:[WiFi] Attempting to connect to SSID: ");
  Serial.println(WIFI_SSID);
  status = WiFi.begin(WIFI_SSID, WIFI_PASS);
  delay(10000);
  if(WL_CONNECTED) {
    IPAddress ip = WiFi.localIP();
    Serial.print("INFO:[WiFi] Connected to SSID: ");
    Serial.print(WIFI_SSID);
    Serial.print(" IP: ");
    Serial.println(ip);
    WIFI_SHIELD = true;
    NETWORKING = true; 
  }
}
 
int freeRam () {
  extern int __heap_start, *__brkval; 
  int v; 
  return (int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval); 
}